package com.example.ohos.architecture.blueprints.todoapp.util;

import com.spotify.dataenum.function.Consumer;
import com.spotify.dataenum.function.Function;

import javax.annotation.Nonnull;

import static com.spotify.dataenum.DataenumUtils.checkNotNull;

/**
 * Either
 *
 * @param <A> a
 * @param <B> b
 */
public abstract class Either<A, B> {
    Either() {
    }

    /**
     * left
     *
     * @param value value
     * @param <A> <A>
     * @param <B> <B>
     * @return Either<A, B>
     */
    public static <A, B> Either<A, B> left(@Nonnull A value) {
        return new Left<A>(value).asEither();
    }

    /**
     * right
     *
     * @param value value
     * @param <A> a
     * @param <B> b
     * @return Either<A, B>
     */
    public static <A, B> Either<A, B> right(@Nonnull B value) {
        return new Right<B>(value).asEither();
    }

    /**
     * isLeft
     *
     * @return boolean
     */
    public final boolean isLeft() {
        return (this instanceof Left);
    }

    /**
     * isRight
     *
     * @return boolean
     */
    public final boolean isRight() {
        return (this instanceof Right);
    }

    /**
     * asLeft
     *
     * @return Left<A>
     */
    @SuppressWarnings("unchecked")
    public final Left<A> asLeft() {
        return (Left<A>) this;
    }

    /**
     * asRight
     *
     * @return Right<B>
     */
    @SuppressWarnings("unchecked")
    public final Right<B> asRight() {
        return (Right<B>) this;
    }

    public abstract void match(@Nonnull Consumer<Left<A>> left, @Nonnull Consumer<Right<B>> right);

    public abstract <R_> R_ map(@Nonnull Function<Left<A>, R_> left,
        @Nonnull Function<Right<B>, R_> right);

    /**
     * Left<A>
     *
     * @param <A> a
     */
    public static final class Left<A> extends Either<A, Object> {
        private final A value;

        Left(A value) {
            this.value = checkNotNull(value);
        }

        /**
         * value
         *
         * @return A
         */
        @Nonnull
        public final A value() {
            return value;
        }

        @Override
        public boolean equals(Object other) {
            if (other == this) {
                return true;
            }
            if (!(other instanceof Left)) {
                return false;
            }
            Left<?> o1 = (Left<?>) other;
            return o1.value.equals(this.value);
        }

        @Override
        public int hashCode() {
            int result = 0;
            return result * 31 + value.hashCode();
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("Left{value=").append(value);
            return builder.append('}').toString();
        }

        @Override
        public final void match(@Nonnull Consumer<Left<A>> left,
                                @Nonnull Consumer<Right<Object>> right) {
            left.accept(this);
        }

        @Override
        public final <R_> R_ map(@Nonnull Function<Left<A>, R_> left,
                                 @Nonnull Function<Right<Object>, R_> right) {
            return left.apply(this);
        }

        /**
         * asEither
         *
         * @param <B> b
         * @return Either<A, B>
         */
        @SuppressWarnings("unchecked")
        public final <B> Either<A, B> asEither() {
            return (Either<A, B>) this;
        }
    }

    /**
     * Right<B>
     *
     * @param <B> b
     */
    public static final class Right<B> extends Either<Object, B> {
        private final B value;

        Right(B value) {
            this.value = checkNotNull(value);
        }

        /**
         * value
         *
         * @return B
         */
        @Nonnull
        public final B value() {
            return value;
        }

        @Override
        public boolean equals(Object other) {
            if (other == this) {
                return true;
            }
            if (!(other instanceof Right)) {
                return false;
            }
            Right<?> o1 = (Right<?>) other;
            return o1.value.equals(this.value);
        }

        @Override
        public int hashCode() {
            int result = 0;
            return result * 31 + value.hashCode();
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("Right{value=").append(value);
            return builder.append('}').toString();
        }

        @Override
        public final void match(@Nonnull Consumer<Left<Object>> left,
            @Nonnull Consumer<Right<B>> right) {
            right.accept(this);
        }

        @Override
        public final <R_> R_ map(@Nonnull Function<Left<Object>, R_> left,
            @Nonnull Function<Right<B>, R_> right) {
            return right.apply(this);
        }

        /**
         * asEither
         *
         * @param <A> aa
         * @return Either<A, B>
         */
        @SuppressWarnings("unchecked")
        public final <A> Either<A, B> asEither() {
            return (Either<A, B>) this;
        }
    }
}
